package net.kleinhenz.norabase.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.StringTokenizer;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.commons.io.IOUtils;

import net.kleinhenz.norabase.server.model.Account;
import net.kleinhenz.norabase.server.model.Battlegroup;
import net.kleinhenz.norabase.server.model.RuneEntry;
import net.kleinhenz.norabase.shared.model.Rune;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserServiceFactory;

public class ImportServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	private static final String TYPE_COLLECTION = "coll";
	private static final String TYPE_BATTLEGROUP = "bg";
	private static final String TYPE_COLLPOX = "pox";
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		this.doGet(req, resp);
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		
		resp.setContentType("text/html");

		User user = UserServiceFactory.getUserService().getCurrentUser();
		String email = null;
		if (user!=null)
			email = user.getEmail();

		String type = null;
		String name = null;
		String data = null;
		Boolean overwrite = null;

		ServletFileUpload upload = new ServletFileUpload();
		upload.setSizeMax(10000000);

		try {
			FileItemIterator iterator = upload.getItemIterator(req);
			while (iterator.hasNext()) {
				FileItemStream item = iterator.next();
				InputStream in = item.openStream();

				if (item.isFormField()) {
					if ("type".equals(item.getFieldName()))
						type = Streams.asString(in);
					else if ("name".equals(item.getFieldName()))
						name = Streams.asString(in);
					else if ("overwrite".equals(item.getFieldName()))
						overwrite = new Boolean(Streams.asString(in));						
				} else {
					try {
						data = IOUtils.toString(in);
					} finally {
						IOUtils.closeQuietly(in);
					}
				}
			}
			if (TYPE_BATTLEGROUP.equals(type))
				importBattlegroup(email, name, data, resp.getWriter());
			else if (TYPE_COLLECTION.equals(type))
				importCollection(email, data, overwrite.booleanValue(), resp.getWriter());						
			else if (TYPE_COLLPOX.equals(type))
				importPoxNoraCollection(email, data, overwrite.booleanValue(), resp.getWriter());						
			else 
				printError("Unknown import type " + type + ".", resp.getWriter());
		} catch (SizeLimitExceededException e) {
			printError("You exceeded the maximum size (" + e.getPermittedSize() + ") of the file (" + e.getActualSize() + ")", resp.getWriter());
		} catch (Exception ex) {
			printError("Error while importing battlegroup. Error was: " + ex.getMessage(), resp.getWriter());
		}
	}

	private void printError(String message, PrintWriter writer) {

		writer.println("<html><body><b>");
		writer.println(message);
		writer.println("</b></body></html>");
		writer.close();
	}
	
	private void importBattlegroup(String email, String name, String data, PrintWriter writer) {
		
		EntityManager em = EMF.get().createEntityManager();
		Account account = retrieveAccount(em, email);
		
		Battlegroup sBG = new Battlegroup();
		sBG.setName(name);
		sBG.setPublicBattlegroup(false);
		sBG.setAccountId(account.getEmail());
		account.getBattlegroups().add(sBG);
		em.persist(sBG);
		em.merge(account);
		em.close();
		
		// This seems to be a bug in app engine: the only way
		// to get the RuneEntry entities to be stored is to
		// close and re-create the em here.
		em = EMF.get().createEntityManager();
		RuneEntry runeEntry = null;
		
		StringTokenizer lt = new StringTokenizer(data, "\n");
		while (lt.hasMoreTokens()) {
			StringTokenizer et = new StringTokenizer(lt.nextToken(), ";");
			String eName = et.nextToken();
			String eType = et.nextToken();
			String eExpansion = et.nextToken();
			Rune rune = PoxNoraXMLReader.searchRune(eType, eName, eExpansion);
			runeEntry = new RuneEntry(rune.getId(), eType);
			sBG.getRunes().add(runeEntry);
			em.persist(runeEntry);
		}
		
		em.merge(sBG);
		em.merge(account);
		em.close();				

		writer.println("<html><body><b>Successfully imported the battlegroup. You can close this window/tab now.</b></body></html>");
	}

	private void importCollection(String email, String data, boolean overwrite, PrintWriter writer) {
		
		EntityManager em = EMF.get().createEntityManager();
		Account account = retrieveAccount(em, email);

		if (overwrite) {
			account.getRunes().clear();
			for (RuneEntry e : account.getRunes()) {			
				em.remove(e);				
			}
			em.merge(account);
		}

		StringTokenizer lt = new StringTokenizer(data, "\n");		
		while (lt.hasMoreTokens()) {
			StringTokenizer et = new StringTokenizer(lt.nextToken(), ";");
			String eName = et.nextToken();
			String eType = et.nextToken();
			String eExpansion = et.nextToken();
			String eCount = et.nextToken();

			if (RuneEntry.search(eName, eExpansion, account.getRunes())==null) {
				Rune rune = PoxNoraXMLReader.searchRune(eType, eName, eExpansion);
				RuneEntry entry = new RuneEntry(rune.getId(), eType);
				entry.setCount(new Integer(eCount));
				account.getRunes().add(entry);
				em.persist(entry);				
			} else {
				RuneEntry entry = RuneEntry.search(eName, eExpansion, account.getRunes());
				entry.setCount(entry.getCount() + new Integer(eCount).intValue());
				em.merge(entry);
			}
		}

		em.merge(account);
		em.close();				

		writer.println("<html><body><b>Successfully imported the collection. You can close this window/tab now.</b></body></html>");
	}

	private void importPoxNoraCollection(String email, String data, boolean overwrite, PrintWriter writer) {
		
		EntityManager em = EMF.get().createEntityManager();
		Account account = retrieveAccount(em, email);

		if (overwrite) {
			account.getRunes().clear();
			for (RuneEntry e : account.getRunes()) {			
				em.remove(e);				
			}
			em.merge(account);
		}

		List<RuneEntry> runeEntries = null;
		try {
			runeEntries = CollectionHTMLImporter.parsePoxNoraHTML(data);
		} catch (IOException e) {
			printError("Error importing collection: " + e.getMessage(), writer);
		}
		for (RuneEntry e : runeEntries) {
		
			if (RuneEntry.search(e.getRuneId(), account.getRunes())==null) {
				account.getRunes().add(e);
				em.persist(e);				
			} else {
				RuneEntry entry = RuneEntry.search(e.getRuneId(), account.getRunes());
				entry.setCount(entry.getCount() + new Integer(e.getCount()).intValue());
				em.merge(entry);
			}
		}

		em.merge(account);
		em.close();				

		writer.println("<html><body><b>Successfully imported the collection. You can close this window/tab now.</b></body></html>");
	}

	private Account retrieveAccount(EntityManager em, String email) {

		Query q = em.createQuery("select from " + net.kleinhenz.norabase.server.model.Account.class.getName() + " where email=:email");
		q.setParameter("email", email);

		Object o = q.getSingleResult();
		if (o!=null)
			return (Account)o;
		else
			// can not happen, accounts are created on login implicitly
			throw new RuntimeException("Account not found: " + email);
	}
}
